package com.txtsqlclient.ui;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.txtsqlclient.dataaccess.DAOException;
import com.txtsqlclient.dataaccess.DAOFactory;
import com.txtsqlclient.dataaccess.DAOType;
import com.txtsqlclient.dataaccess.database.QueryExecutionDAO;
import com.txtsqlclient.dataaccess.database.connection.ConnectionDetailsVO;
import com.txtsqlclient.dataaccess.database.connection.ConnectionException;

/**
 * @author spadda
 */
public class Sql {

    ConnectionDetailsVO conDetails = null;

    private static int width = 170;

    public void setConnectionDetails(ConnectionDetailsVO conDetails) {
        this.conDetails = conDetails;
    }

    public static void main(String[] args) throws ConnectionException, IOException {
        Sql s = new Sql();
        s.verifyConnection();
        while (true) {
            System.out.print("sql> ");
            BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
            String query = br.readLine();
            if (query.equals("exit")) {
                s.exit();
                break;
            }
            s.handleQuery(query);
        }
    }

    /**
     * This method is required to construct a connection details object, from a user's input.
     * This method is required to take input from the user interactively, instead of reading it
     * from the command prompt.
     * 
     * @return the connection details, to open a database connection.
     * @throws IOException
     */
    public ConnectionDetailsVO readConnectionDetails() throws IOException {
        ConnectionDetailsVO connectionDetails = new ConnectionDetailsVO();
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        connectionDetails.setDbType(UserInputCapturer.getLine("Please enter the type of database:"));
        connectionDetails.setIp(UserInputCapturer.getLine("Please enter the IP/hostname of the database server:"));
        connectionDetails.setDatabase(UserInputCapturer.getLine("Please enter the database instance to connect to:"));
        connectionDetails.setUser(UserInputCapturer.getLine("Please enter the username:"));
        connectionDetails.setPassword(new String(
                UserInputCapturer.getSecureMaskedLine("Please enter the password: ")));
        setConnectionDetails(connectionDetails);
        return connectionDetails;
    }

    /**
     * This method is required to construct a connection details object, from the user's input.
     * 
     * @param args
     *            the command-line arguments specified by the user.
     * @return the connection details, needed to open a database connection.
     */
    public ConnectionDetailsVO getConnectionDetails(String[] args) {
        ConnectionDetailsVO connectionDetails = new ConnectionDetailsVO();
        connectionDetails.setDbType(args[0]);
        connectionDetails.setIp(args[1]);
        connectionDetails.setDatabase(args[2]);
        connectionDetails.setUser(args[3]);
        connectionDetails.setPassword(args[4]);
        return connectionDetails;
    }

    public void handleQuery(String query) {
        Pattern firstWordPattern = Pattern.compile("([A-Za-z]+)");
        Matcher m = firstWordPattern.matcher(query);
        boolean find = m.find();
        String firstWord = m.group(1);
        try {
            if ("SELECT".equalsIgnoreCase(firstWord)) {
                List<List<String>> data = this.executeSelectQuery(query);
                System.out.println(TabularLayoutHelper.print(data, width));
                // Need to modify the data structure for the result, by having list of rows
                // instead of list of columns.
                System.out.println("Displaying " + (data.get(0).size() - 1) + " rows");
            } else {
                int rows = this.executeUpdateQuery(query);
                System.out.println("Modified " + rows + " rows");
            }
        } catch (DAOException e) {
            System.out.println("Error while executing the query: " + query);
            e.printStackTrace();
        }
    }

    public List<List<String>> executeSelectQuery(String query) throws DAOException {
        List<List<String>> result = null;
        DAOFactory daoFactory = DAOFactory.getInstance(this.conDetails);
        QueryExecutionDAO dao = daoFactory.getDAO(DAOType.GenericDAO);
        result = dao.read(query);
        return result;
    }

    public int executeUpdateQuery(String query) throws DAOException {
        int rows = 0;
        DAOFactory daoFactory = DAOFactory.getInstance(this.conDetails);
        QueryExecutionDAO dao = daoFactory.getDAO(DAOType.GenericDAO);
        rows = dao.write(query);
        return rows;
    }

    private void verifyConnection() throws IOException {
        boolean success = this.conDetails != null;
        while (!success) {
            try {
                readConnectionDetails();
                DAOFactory.getInstance(this.conDetails);
                success = true;
            } catch (DAOException e) {
                System.out.println("There is some problem connecting to the database, with the following details"
                        + this.conDetails
                        + ".");
                System.out.println("Please verify the login information, or try again later.");
                e.printStackTrace();
            }
        }
    }

    private void exit() {
        try {
            DAOFactory.cleanUp();
        } catch (DAOException e) {
            System.out.println("Error while closing the database connection");
            e.printStackTrace();
        }
    }
}